#!/usr/bin/python 
#===========================================================================#
# conf2csv, Part of the csv2conf package -- Copyright 2011 by Martin Ponweiser
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#-----------------------------------------------------------------------------#
"""
conf2csv,
reverse, too:
input needs order column for easier sorting
ini-parser, normal equals, space,

one layer of markup and content only.
specify options for each markup on command line
options: sep=, comment_prefix=, comment_mode, lookup_file (reverse)
folder, 
if existing, read in previous file into dict. markup should be should be stored with larger int key interval to allow later insertion.
read in new file into dict
match necessary markup rows. break if duplicates are detected later. extend/add where required
which first?
"""

import csv
import string # excel_x()
import re
import os.path # pathnames 
import os # makedirs()
from optparse import OptionParser # deprecated in favor of argparse, however I want to support old Python versions.
import logging

MARKUP_PREFIX = "csv2conf_"
MARKUP_FILE_NAME = "file_name"
MARKUP_FILE_FOLDER = "file_folder"
MARKUP_OUTPUT_MODE = "output_mode"
MARKUP_OUTPUT_BACKUP = "output_backup"
MARKUP_FILE_FORMAT = "file_format"
MARKUP_FORMAT_DEFAULT = "format_default"
MARKUP_COLUMN = "column"

COLUMNS_VALID = ['identifier','value','format','comment','order']
MODES_VALID = ['rewrite','folder_parents_create','merge','append']
FILE_FORMATS_VALID = ['linebreaks_dos','linebreaks_unix']
FORMAT_OPTIONS_VALID = re.compile(r"\W*(sep|comment_prefix|comment_mode|value_lookup_file)([=:])(.+)",flags=re.IGNORECASE)
FORMAT_FLAGS_VALID = re.compile(r"\W*(output_id_only|format_default)",flags=re.IGNORECASE)
COMMENT_MODES_VALID = ['insert_before_line','append_to_value']
BACKUPS_VALID = ['yes']

def dict_invert(ddict):
    # http://code.activestate.com/recipes/252143-invert-a-dictionary-one-liner/
    ans = {}
    for kkey, vvalue in ddict.iteritems():
        anslist = ans.setdefault(vvalue, [])
        anslist.append(kkey)
    return ans

def dict_invert_list(ddict):
    ans = {}
    for kkey, llist in ddict.iteritems():
        for iitem in llist:
            anslist = ans.setdefault(iitem, [])
            anslist.append(kkey)
    return ans

def excel_x(iindex):
    ## see: Python Cookbook 2nd ed., Recipe 18.2 
    result = [ ]
    addon = result.append
    while iindex > 26:
        iindex, rrem = divmod(iindex, 26)
        addon(string.ascii_uppercase[rrem-1])
    addon(string.ascii_uppercase[iindex-1])
    result.reverse()
    return ''.join(result)

def format_parse(format_string):
    result = {}
    format_list = list(csv.reader([format_string]))[0]
    for fformat in format_list:
        matched = FORMAT_OPTIONS_VALID.match(fformat)
        if matched:
            matched_key = matched.group(1).lower()
            # overwrite previous entries
            result[matched_key] = matched.group(3)
            continue
        matched = FORMAT_FLAGS_VALID.match(fformat)
        if matched:
            matched_key = matched.group(1).lower()
            result[matched_key] = ""
            continue
        if 'sep' not in result:
            # Take fformat as separator. The last non-matched format token is therefore the sep!
            result['sep'] = fformat
    return result

def int_or_str(sstring):
    try:
        return(int(sstring))
    except ValueError:
        return(sstring)

def output_buffer_sort(buffer_dict, column_index, logger):
    for ffilename in buffer_dict:
        # see conditional expression and sorting mini-howto.
        # sorting is stable, which means that empty cells will not be mixed.
        if 'order' in column_index[ffilename]:
            logger.info("%s: Sorting." % ffilename)
            ## first try to interprete value as int, this makes 
            ## order: 1. if can be interpreted as int, order as int, 2. order by strings, 3. '' keep order, append at
            ## list end (stable sorting)
            buffer_dict[ffilename].sort(key=lambda x: int_or_str(x['order'][0]) if x['order'][0]!="" else 'zzzzzzzzzzz')

def main():
    #csv.register_dialect('tab_quote_no', delimiter='\t', quoting=csv.QUOTE_NONE)
    #csv.register_dialect('comma_quote_double', delimiter=',', quoting=csv.QUOTE_NONE)

    parser = OptionParser(usage="usage: %prog [options] file1.csv file2.csv ..."
)
    parser.add_option("-d", "--csv-dialect",
                      default="excel",
                      dest="csv_dialect",
                      help="How to handle CSV field separators and quotes. Valid are: %s. " % \
                      ', '.join(csv.list_dialects()) + '[default: %default]')
    parser.add_option("-w", "--warnings-show",
                      action="store_true",
                      dest="stdout_warnings_show",
                      default=False,
                      help="show warnings on stdout. [default:no warnings]")
    parser.add_option("-l", "--log-filename",
                      default="",
                      dest="log_filename",
                      help="If provided, append all logging (including DEBUG and WARNING) to this file.")
#options: sep=, comment_prefix=, comment_mode, lookup_file (reverse)
#folder, 

    (options, args) = parser.parse_args()

    # DEBUG defaults
    #args = ['examples/arcadesetup-ooo-defaults.csv']

    logger = logging.getLogger('conf2csv')
    logger.setLevel(logging.DEBUG)

    loghandler_stdout = logging.StreamHandler()
    logformatter = logging.Formatter('%(levelname)-8s %(message)s')
    loghandler_stdout.setFormatter(logformatter)
    
    ## http://docs.python.org/howto/logging-cookbook.html#logging-cookbook
    ## DEBUG: never, WARNING: option, INFO/ERROR/CRITICAL: always
    class FilterStdout(logging.Filter):
        def __init__(self, warnings_show):
            self.warnings_show = warnings_show
            pass
        def filter(self, record):
            ## http://docs.python.org/library/logging.html#logrecord-attributes
            if record.levelno == logging.DEBUG:
                return False
            if record.levelno == logging.WARNING and self.warnings_show == False:
                return False
            return True

    loghandler_stdout.addFilter(FilterStdout(options.stdout_warnings_show))
    logger.addHandler(loghandler_stdout)

    if options.log_filename:
        loghandler_logfile = logging.FileHandler(options.log_filename)
        logformatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        loghandler_logfile.setFormatter(logformatter)
        logger.addHandler(loghandler_logfile)
        logger.info('============== Script started. ==============')
        logformatter = logging.Formatter('%(levelname)-8s %(message)s')
        loghandler_logfile.setFormatter(logformatter)

    """
    # 'application' code
    logger.debug('debug message')
    logger.info('info message')
    logger.warn('warn message')
    logger.error('error message')
    logger.critical('critical message')
    """
    for csv_filename in args:
        with open(csv_filename, 'Urb') as csv_file:
            logger.info("------------ %s ------------" % csv_filename)
            #reader_process(csv.reader(csv_file, dialect=options.csv_dialect), logger)
    logger.info('Done.')

if __name__ == '__main__':
    main()
